/*
 * =====================================================================================
 *
 *       Filename:  commands.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  22/10/2014 23:17:08
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Benjamin Arbe (BA), 
 *   Organization:  
 *
 * =====================================================================================
 */

#include <apr_uri.h>
#include <apr_fnmatch.h>
#include <unistd.h>

#include "commands.h"
#include "dbg.h"
#include "bstrlib.h"
#include "db.h"
#include "shell.h"

int
Command_depends (apr_pool_t *p, const char *path) {
    FILE *in = NULL;
    bstring line = NULL;

    in = fopen (path, "r");
    check (in != NULL, "Failed to open downloaded depends: %s", path);

    for (line = bgets((bNgetc)fgetc, in, '\n'); line != NULL;
            line = bgets ((bNgetc)fgetc, in, '\n')) {
        btrimws (line);
        log_info ("Processing depends: %s", bdata(line));
        int rc = Command_install (p, bdata(line), NULL, NULL, NULL);
        check (rc == 0, "Failed to install: %s", bdata(line));
        bdestroy (line);
    }

    fclose (in);
    return 0;

error:
    if (line) bdestroy (line);
    if (in) fclose (in);
    return -1;
}

int
Command_fetch (apr_pool_t *p, const char *url, int fetch_only) {
    apr_uri_t info = {.port = 0};
    int rc = 0;
    const char *depends_file = NULL;
    apr_status_t rv = apr_uri_parse (p, url, &info);

    check (rv == APR_SUCCESS, "failed to parse URL: %s", url);

    if (apr_fnmatch (GIT_PAT, info.path, 0) == APR_SUCCESS) {
        rc = Shell_exec (GIT_SH, "URL", url, NULL);
        check (rc == 0, "git failed.");
    } else if (apr_fnmatch (DEPEND_PAT, info.path, 0) == APR_SUCCESS) {
        check (!fetch_only, "No point in fetching a DEPENDS file.");

        if (info.scheme) {
            depends_file = DEPENDS_PATH;
            rc = Shell_exec (CURL_SH, "URL", url, "TARGET", depends_file, NULL);
            check (rc == 0, "Curl failed.");
        } else {
            depends_file = info.path;
        }
   
        // recursively process the devpkg list
        log_info ("Building according to DEPENDS: %s", url);
        rv = Command_depends (p, depends_file);
        check (rv == 0, "Failed to process the DEPENDS: %s", url);

        // this indicates that nothing needs to be done.
        return 0;
    } else if (apr_fnmatch (TAR_GZ_PAT, info.path, 0) == APR_SUCCESS) {
        if (info.scheme) {
            rc = Shell_exec (CURL_SH,
                    "URL", url,
                    "TARGET", TAR_GZ_SRC, NULL);
            check (rc == 0, "Failed to curl source: %s", url);
        }
        rv = apr_dir_make_recursive (BUILD_DIR,
                APR_UREAD | APR_UWRITE | APR_UEXECUTE, p);
        check (rv == APR_SUCCESS, "Failed to make directory %s", BUILD_DIR);

        rc = Shell_exec (TAR_SH, "FILE", TAR_GZ_SRC, NULL);
        check (rc == 0, "Failed to untar %s", TAR_GZ_SRC);
    } else if (apr_fnmatch (TAR_BZ2_PAT, info.path, 0) == APR_SUCCESS) {
        if (info.scheme) {
            rc = Shell_exec (CURL_SH, "URL", url, "TARGET", TAR_BZ2_SRC, NULL);
            check (rc == 0, "Curl failed.");
        }

        apr_status_t rc = apr_dir_make_recursive (BUILD_DIR,
                APR_UREAD | APR_UWRITE | APR_UEXECUTE, p);

        check (rc == 0, "Failed to make directory %s", BUILD_DIR);
        rc = Shell_exec (TAR_BZ2_SH, "FILE", TAR_BZ2_SRC, NULL);
        check (rc == 0, "Failed to untar %s", TAR_BZ2_SRC);
    } else if (apr_fnmatch (TAR_XZ_PAT, info.path, 0) == APR_SUCCESS) {
        if (info.scheme) {
            rc = Shell_exec (CURL_SH, "URL", url, "TARGET", TAR_XZ_SRC, NULL);
            check (rc == 0, "Curl failed.");
        }
        
        apr_status_t rc = apr_dir_make_recursive (BUILD_DIR,
                APR_UREAD | APR_UWRITE | APR_UEXECUTE, p);
        
        check (rc == 0, "Failed to make directory %s", BUILD_DIR);
        rc = Shell_exec (TAR_XZ_SH, "FILE", TAR_XZ_SRC, NULL);
        check (rc == 0, "Failed to untar %s", TAR_XZ_SRC);
        
    } else {
        sentinel ("Don't now how to handle %s", url);
    }

    // indicates that an install needs to actually run
    return 1;
error:
    return -1;
}

int
Command_build (apr_pool_t *p, const char *url, const char *configure_opts,
        const char *make_opts, const char *install_opts) {
    int rc = 0;

    check (access (BUILD_DIR, X_OK | R_OK | W_OK) == 0,
            "Build directory doesn't exist: %s", BUILD_DIR);

    // actually do an install
    if (access (CONFIG_SCRIPT, X_OK) == 0) {
        log_info ("Has a configure script, running it.");
        rc = Shell_exec (CONFIGURE_SH, "OPTS", configure_opts, NULL);
        check (rc == 0, "Failed to configure.");
    }

    rc = Shell_exec (MAKE_SH, "OPTS", make_opts, NULL);
    check (rc == 0, "Failed to build.");

    rc = Shell_exec (INSTALL_SH,
            "TARGET", install_opts ? install_opts : "install", NULL);
    check (rc == 0, "Failed to install.");

    rc = Shell_exec (CLEANUP_SH, NULL);
    check (rc == 0, "Failed to cleanup after build.");

    rc = DB_update (url);
    check (rc == 0, "Failed to add this package to the database.");

    return 0;

error:
    return -1;
}

int
Command_install (apr_pool_t *p, const char *url, const char *configure_opts,
        const char *make_opts, const char *install_opts) {
    int rc = 0;
    check (Shell_exec (CLEANUP_SH, NULL) == 0, "Failed to cleanup before \
            building.");

    rc = DB_find (url);
    check (rc != -1, "Error checking the install database.");

    if (rc == 1) {
        log_info ("Package %s already installed.", url);
        return 0;
    }

    rc = Command_fetch (p, url, 0);

    if (rc == 1) {
        rc = Command_build (p, url, configure_opts, make_opts, install_opts);
        check (rc == 0, "Failed to build: %s", url);
    } else if (rc == 0) {
        // no install needed
        log_info ("Depends successfully installed: %s", url);
    } else {
        // had an error
        sentinel ("Install failed: %s", url);
    }

    Shell_exec (CLEANUP_SH, NULL);
    return 0;

error:
    Shell_exec (CLEANUP_SH, NULL);
    return -1;
}

int
Command_cleanup () {
    Shell_exec (CLEANUP_SH, NULL);
    return 0;
}



